﻿namespace SenenFernandez.Sensktop
{
    using SenenFernandez.Sensktop.Configuration;
    using SenenFernandez.Sensktop.Enum;
    using SenenFernandez.SmartExtensionMethods;
    using SenenFernandez.SmartExtensionMethods.Interactivity;
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO;
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Interop;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Xml.Serialization;

    public class Application : NotificationObject
    {
        private ICommand clickCommand;
        private ICommand closeCommand;

        /// <summary>
        /// Gets the parent application.
        /// </summary>
        [XmlIgnore]
        public Application Parent { get; private set; }

        /// <summary>
        /// Gets the Level.
        /// </summary>
        [XmlIgnore]
        public int Level { get; private set; }

        /// <summary>
        /// Gets the unique identifier of this application.
        /// </summary>
        [XmlIgnore]
        public Guid Id { get; private set; }

        /// <summary>
        /// Gets the process.
        /// </summary>
        [XmlIgnore]
        public Process Process { get; private set; }

        /// <summary>
        /// Gets the process instances.
        /// </summary>
        [XmlIgnore]
        public ConcurrentDictionary<int, Process> ProcessInstances { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether the user interface of the process is not responding.
        /// </summary>
        [XmlIgnore]
        public int NotRespondingCount { get; private set; }

        /// <summary>
        /// Gets the Status of this application.
        /// </summary>
        [XmlIgnore]
        public ApplicationStatus ApplicationStatus { get; private set; }

        /// <summary>
        /// Gets the Type of this application.
        /// </summary>
        public ApplicationType ApplicationType { get; set; }

        /// <summary>
        /// Gets the Name of this application.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Gets the Description of this application.
        /// This will be displayed on the Tooltip UI control.
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// Gets the Title of this application.
        /// This will be displayed on the Header UI control.
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// Gets the FileName of this application.
        /// </summary>
        public string FileName { get; set; }

        /// <summary>
        /// Gets the Arguments to open this application.
        /// </summary>
        public string Arguments { get; set; }

        /// <summary>
        /// Gets or sets icon URI for item, linked to IconPath element in Application at XML menu configuration file.
        /// </summary>
        public string IconPath { get; set; }

        /// <summary>
        /// Returns true if this is a multi instance window.
        /// </summary>
        public bool IsMultiInstance { get; set; }

        /// <summary>
        /// Gets a value indicating whether automatically launch this application or not.
        /// </summary>
        public bool IsAutorun { get; set; }

        /// <summary>
        /// Gets a value indicating whether automatically restart this application or not.
        /// </summary>
        public bool IsAutoRestart { get; set; }

        /// <summary>
        /// Gets or sets the wait for input Idle.
        /// </summary>
        public int Timeout { get; set; }

        /// <summary>
        /// Gets the number of attempts to execute this application.
        /// </summary>
        public int RetryCount { get; set; }

        /// <summary>
        /// Gets the time interval in milliseconds between two subsequent attempts to execute this application.
        /// </summary>
        public int RetryDelay { get; set; }

        /// <summary>
        /// Gets the max number of attempts before to kill this application.
        /// </summary>
        public int MaxNotRespondingCount { get; set; }

        /// <summary>
        /// Gets a value indicating whether is console application or not.
        /// </summary>
        public bool IsConsole
        {
            get { return ApplicationType == ApplicationType.Console; }
        }

        /// <summary>
        /// Gets a value indicating whether this application has any child application.
        /// </summary>
        public bool HasChildApplications
        {
            get { return Applications != null && Applications.Count > 0; }
        }

        /// <summary>
        /// Gets the Application childrens.
        /// </summary>
        public List<Application> Applications { get; set; }

        /// <summary>
        /// Gets or sets icon URI for item, linked to IconPath element in Application at XML menu configuration file.
        /// Optional, can use target icon if no available
        /// </summary>
        public ImageSource IconSource
        {
            get
            {
                ImageSource imageSource = null;
                if (!string.IsNullOrEmpty(IconPath))
                {
                    imageSource = IconPath.ToImageSource();
                }
                else
                {
                    string path = FileName;
                    if (!File.Exists(path))
                    {
                        var file = Path.GetFileName(FileName);
                        if (file != null)
                        {
                            path = Path.Combine(Environment.SystemDirectory, file);
                            if (!File.Exists(path))
                            {
                                path = null;
                            }
                        }
                    }
                    if (!string.IsNullOrEmpty(path))
                    {
                        var extractedIcon = Icon.ExtractAssociatedIcon(path);
                        if (extractedIcon != null)
                        {
                            using (var icon = Icon.FromHandle(extractedIcon.ToBitmap().GetHicon()))
                            {
                                imageSource = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, Int32Rect.Empty,
                                    BitmapSizeOptions.FromEmptyOptions());
                            }
                        }
                    }
                }
                return imageSource;
            }
        }

        /// <summary>
        /// On click command.
        /// </summary>
        public ICommand ClickCommand
        {
            get { return clickCommand ?? (clickCommand = new DelegateCommand(OnClick)); }
        }

        /// <summary>
        /// Closes the window.
        /// </summary>
        public ICommand CloseCommand
        {
            get { return closeCommand ?? (closeCommand = new DelegateCommand(OnClose)); }
        }

        /// <summary>
        /// Initializes a new instance of the Application class.
        /// </summary>
        public Application()
        {
            Id = Guid.NewGuid();
            Level = 1;
            Timeout = DefaultValue.ApplicationTimeout;
            RetryCount = DefaultValue.ApplicationRetryCount;
            RetryDelay = DefaultValue.ApplicationRetryDelay;
            MaxNotRespondingCount = DefaultValue.ApplicationMaxNotRespondingCount;
        }

        /// <summary>
        /// Launch this application.
        /// </summary>
        public void Autorun()
        {
            OnClick();
        }

        /// <summary>
        /// Set parent Application.
        /// </summary>
        public void SetParent(Application parent)
        {
            if (Parent != parent)
            {
                Parent = parent;
                Level = parent.Level + 1;
            }
        }

        /// <summary>
        /// Opens the UI window.
        /// </summary>
        public void Open()
        {
            try
            {
                if (!HasChildApplications)
                {
                    if (Process == null || Process.HasExited)
                    {
                        StartProcess();
                        ProcessExplorer.Instance.Add(this);
                    }
                    else
                    {
                        if (IsMultiInstance)
                        {
                            if (ProcessInstances == null)
                            {
                                ProcessInstances = new ConcurrentDictionary<int, Process>();
                            }
                            var instance = ProcessExplorer.Instance.StartNewProcess(this);
                            ProcessInstances.AddOrUpdate(instance.Id, instance, (key, value) => instance);
                        }
                        else
                        {
                            Process.MainWindowHandle.RestoreWindow();
                        }
                    }
                    CloseWindow(true);
                }
                RaisePropertyChanged(() => IsSelected);
            }
            catch (Exception exception)
            {
                exception.Show();
            }
        }

        /// <summary>
        /// Closes the UI window.
        /// </summary>
        public void Close()
        {
            try
            {
                CloseWindow();
                if (!HasChildApplications)
                {
                    ProcessExplorer.Instance.Remove(this);
                }
            }
            catch (Exception exception)
            {
                exception.Log();
            }
        }

        /// <summary>
        /// Closes the UI window.
        /// </summary>
        public void CloseWindow(bool closeParent = false)
        {
            try
            {
                var applications = Applications.SelectHierarchy(list => list.Applications, source => true);
                foreach (var application in applications)
                {
                    application.CloseWindow();
                }
                if (Parent != null && closeParent)
                {
                    Parent.CloseWindow(true);
                }
            }
            catch (Exception exception)
            {
                exception.Log();
            }
        }

        /// <summary>
        /// Sets a value indicating whether the user interface of the process is not responding.
        /// </summary>
        public void SetNotRespondingCount(int notRespondingCount)
        {
            if (NotRespondingCount != notRespondingCount)
            {
                NotRespondingCount = NotRespondingCount;
                RaisePropertyChanged(() => NotRespondingCount);
            }
        }

        /// <summary>
        /// Sets the Status of this application.
        /// </summary>
        public void SetApplicationStatus(ApplicationStatus applicationStatus)
        {
            if (ApplicationStatus != applicationStatus)
            {
                ApplicationStatus = applicationStatus;
                RaisePropertyChanged(() => ApplicationStatus);
            }
        }

        /// <summary>
        /// Starts the process.
        /// </summary>
        public void StartProcess()
        {
            Process = ProcessExplorer.Instance.StartNewProcess(this);
        }

        /// <summary>
        /// Clear all process.
        /// </summary>
        public void ClearProcesses()
        {
            if (ProcessInstances != null)
            {
                ProcessInstances.Clear();
                ProcessInstances = null;
            }
            Process = null;
            SetApplicationStatus(ApplicationStatus.None);
        }

        private void OnClick()
        {
            Open();
        }

        private void OnClose()
        {
            Close();
        }
    }
}